# book_client.py
# gRPC客户端实现

import grpc
import time
import book_service_pb2
import book_service_pb2_grpc


def run():
    """
    运行gRPC客户端
    """
    # 创建一个gRPC通道连接到服务器
    # 使用insecure_channel表示不使用SSL/TLS加密
    with grpc.insecure_channel("192.168.30.109:50051") as channel:
        # 创建一个stub（存根）用于调用服务
        stub = book_service_pb2_grpc.BookServiceStub(channel)

        # 演示1: 简单RPC - 获取单本图书
        print("\n=== 演示1: 获取单本图书 ===")
        try:
            # 创建一个请求对象
            book_request = book_service_pb2.BookRequest(id=1)
            # 调用远程方法
            book = stub.GetBook(book_request)
            print(
                f"收到图书: ID={book.id}, 标题={book.title}, 作者={book.author}, 年份={book.year}"
            )

            # 尝试获取一个不存在的图书
            book_request = book_service_pb2.BookRequest(id=99)
            book = stub.GetBook(book_request)
            print("这行代码不会执行，因为前面会抛出异常")
        except grpc.RpcError as e:
            # 处理gRPC错误
            print(f"RPC错误: {e.code()}: {e.details()}")

        # 演示2: 服务器流式RPC - 获取所有图书
        print("\n=== 演示2: 获取所有图书 ===")
        try:
            # 创建一个空请求
            empty = book_service_pb2.Empty()
            # 调用流式方法，返回一个迭代器
            books_iterator = stub.ListBooks(empty)

            # 遍历响应流
            for book in books_iterator:
                print(
                    f"收到图书: ID={book.id}, 标题={book.title}, 作者={book.author}, 年份={book.year}"
                )
        except grpc.RpcError as e:
            print(f"RPC错误: {e.code()}: {e.details()}")

        # 演示3: 客户端流式RPC - 添加多本图书
        print("\n=== 演示3: 添加多本图书 ===")
        try:
            # 创建一个生成器函数来产生请求流
            def generate_books():
                # 定义要添加的新书
                new_books = [
                    book_service_pb2.Book(
                        id=4, title="机器学习实战", author="赵六", year=2022
                    ),
                    book_service_pb2.Book(
                        id=5, title="Web开发入门", author="钱七", year=2023
                    ),
                ]
                # 逐个生成Book对象
                for book in new_books:
                    print(f"发送图书: {book.title}")
                    yield book
                    time.sleep(0.5)

            # 调用客户端流式方法，传入生成器
            response = stub.AddBooks(generate_books())
            print(f"添加图书结果: {response.message}")
        except grpc.RpcError as e:
            print(f"RPC错误: {e.code()}: {e.details()}")

        # 演示4: 双向流式RPC - 实时获取图书更新
        print("\n=== 演示4: 实时获取图书更新 ===")
        try:
            # 创建一个生成器函数来产生请求流
            def generate_requests():
                # 要查询的图书ID列表
                book_ids = [1, 99, 2, 3, 4]
                for book_id in book_ids:
                    request = book_service_pb2.BookRequest(id=book_id)
                    print(f"请求图书ID: {book_id}")
                    yield request
                    time.sleep(1)

            # 调用双向流式方法
            responses = stub.WatchBooks(generate_requests())

            # 处理响应流
            for book in responses:
                print(f"收到图书更新: ID={book.id}, 标题={book.title}")
        except grpc.RpcError as e:
            print(f"RPC错误: {e.code()}: {e.details()}")


if __name__ == "__main__":
    run()
